/*
 * QTXpost - a Qt-based blog client.
 * Copyright (C) 2010 Helen Sommer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (in COPYING); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 *
 * The project is hosted on http://code.google.com/p/qtxpost
 */

#include "FriendsManager.h"
#include "MainWindow.h"
#include "LabelMenu.h"
#include "GroupsTree.h"
#include "UsersList.h"

extern MainWindow *root;

FriendsManager::FriendsManager(QWidget *parent) 
  : QDialog(parent)
{
  setupUi(this);

  //friendsScrollArea->setWidgetResizable(false); // 'true' makes everything appear tiny

  groupsTree = new GroupsTree(this);
  groupsTree->setObjectName(QString::fromUtf8("groupsTree"));
  groupsTree->setAcceptDrops(true);
  groupsScrollArea->setWidget(groupsTree);

  usersList = new UsersList(this);
  usersList->setObjectName(QString::fromUtf8("usersList"));
  usersList->setAcceptDrops(false);
  usersScrollArea->setWidget(usersList);

  contentFiltersTree = new GroupsTree(this);
  contentFiltersTree->setObjectName(QString::fromUtf8("contentFiltersTree"));
  contentFiltersTree->setAcceptDrops(true);
  contentFiltersScrollArea->setWidget(contentFiltersTree);

  watchedUsersList = new UsersList(this);
  watchedUsersList->setObjectName(QString::fromUtf8("watchedUsersList"));
  watchedUsersList->setAcceptDrops(false);
  contentFiltersScrollArea_2->setWidget(watchedUsersList);

  server = 0;
  serverType = NO_TYPE;
  updated = false;
  initInProgress = false;
  currentTab = 0;

  editGroup = new EditGroup(this);
  editUser = new EditUser(this);

  journalTypesCombo->addItem(tr("Users"));
  journalTypesCombo->addItem(tr("Communities"));
  journalTypesCombo->addItem(tr("RSS"));
  journalTypesCombo->addItem(tr("All"));

  friendTypesCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
  
  trustToggle->hide();
  watchToggle->hide();


  QFrame *friendsForm = new QFrame(this);
  friendsScrollArea->setWidget(friendsForm);

  friendsLayout = new QGridLayout();
  friendsLayout->setSpacing(0);
  friendsForm->setLayout(friendsLayout);


  connect (tabs, SIGNAL(currentChanged(int)), this, SLOT(switchTab(int)));
  connect (tabs, SIGNAL(currentChanged(int)), editGroup, SLOT(switchMode(int)));
  connect (closeButton, SIGNAL(clicked()), this, SLOT(close()));
  connect (updateButton, SIGNAL(clicked()), this, SLOT(updateData()));
  connect (saveButton, SIGNAL(clicked()), this, SLOT(saveChanges()));
  connect (addButton, SIGNAL(clicked()), this, SLOT(addStart()));
  connect (usernameLine, SIGNAL(returnPressed()), this, SLOT(addStart()));

  connect (friendTypesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(initializeFriends(int))); 
  connect (journalTypesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(initializeFriends(int)));

  /// connect (addGroupButton, SIGNAL(clicked()), this, SLOT(addGroupStart()));
    ///connect (addContentFilterButton, SIGNAL(clicked()), this, SLOT(addContentFilterStart()));
    ///connect (addContentFilterLine, SIGNAL(returnPressed()), this, SLOT(addContentFilterStart()));

  serversCombo->addItem("-----");
  for (int i = 0; i < root->servers.size(); i++) {
    if (root->servers.at(i)->hidden || root->servers.at(i)->deleted) continue;
    serversCombo->addItem(root->servers.at(i)->label);
  }
  connect(serversCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(switchServer(QString)));
  
  trustButtonGroup = new QButtonGroup(); // to get clicked checkboxes' IDs
  trustButtonGroup->setExclusive(false);
  connect(trustButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(toggleUserTrust(int)));  

  watchButtonGroup = new QButtonGroup(); // to get clicked checkboxes' IDs
  watchButtonGroup->setExclusive(false);
  connect(watchButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(toggleUserWatch(int)));  

  headerCommunities << tr("Community") << tr("Full name") << tr("Subscribed") << tr("Member");

  friendTypesLj[T_FRIEND] = tr("Friends");
  friendTypesLj[T_MUTUAL] = tr("Mutual");
  friendTypesLj[T_FRIEND_OF] = tr("Friends of");
  friendTypesLj[T_ALL] = tr("All"); 
  
  friendTypesDw[T_TRUST_OR_WATCH] = tr("Trust or watch");
  friendTypesDw[T_TRUST_AND_WATCH] = tr("Trust and watch");
  friendTypesDw[T_TRUST] = tr("Trust");
  friendTypesDw[T_TRUSTED] = tr("Trusted");
  friendTypesDw[T_MUTUAL_TRUST] = tr("Mutual trust");
  friendTypesDw[T_WATCH] = tr("Watch");
  friendTypesDw[T_WATCHED] = tr("Watched");
  friendTypesDw[T_MUTUAL_WATCH] = tr("Mutual watch");
  friendTypesDw[T_TRUSTED_WATCHED] = tr("Trusted and watched");
  friendTypesDw[T_TW_ALL] = tr("All");

}

void FriendsManager::switchTab(int index)
{
  // save button only needed for friend manager when trust/watch flags are changed
  // otherwise the changes are instant

  switch (index) {
  case MODE_USERS:
    saveButton->show();
    friendTypeLabel->show();
    friendTypesCombo->show();
    if (serverType == DW_TYPE) {
      trustToggle->show();
      watchToggle->show();
    }
    else {
      trustToggle->hide();
      watchToggle->hide();
    }
    usernameLabel->setText(tr("Username:"));
    break;
  case MODE_TRUSTGROUPS:
    saveButton->hide();
    friendTypeLabel->hide();
    friendTypesCombo->hide();
    trustToggle->hide();
    watchToggle->hide();
    usernameLabel->setText(tr("Group name:"));
    break;
  case MODE_CONTENTFILTERS:
    saveButton->hide();
    friendTypeLabel->hide();
    friendTypesCombo->hide();
    trustToggle->hide();
    watchToggle->hide();
    usernameLabel->setText(tr("Group name:"));
    break;
  }
  usernameLine->clear();
  currentTab = index;
}

void FriendsManager::updateServer(QString label)
{
  for (int i = 0; i < root->servers.size(); i++) {
    if (root->servers.at(i)->label == label) {
      server = root->servers.at(i);
      setCursor(Qt::WaitCursor);
      if (server->serverType == LJ_TYPE) {
        server->startGetFriends();
      }
      else {
        server->startGetCircle();
      }
      break;
    }
  }
}

void FriendsManager::initializeHeaders(int type)
{
  // switch between DW- and LJ-specific headers and widgets

  cleanupFriends();
  friendTypesCombo->clear();  
  headerUsers.clear();

  if (type == DW_TYPE) {
    headerUsers << tr("Username") << tr("Full name") << tr("Trust") << tr("Watch") << tr("Trusted") << tr("Watched");
    for (int i = 0; i < friendTypesDw.count(); i++) {
      friendTypesCombo->addItem(friendTypesDw.value(i));
    }
    gridColumns = 6;
    trustToggle->show();
    watchToggle->show();
    tabs->setTabEnabled(2, true);
  }
  else {
    headerUsers << tr("Username") << tr("Full name") << tr("Friend") << tr("Friend of");
    for (int i = 0; i < friendTypesLj.count(); i++) {
      friendTypesCombo->addItem(friendTypesLj.value(i));
    }
    gridColumns = 4;
    trustToggle->hide();
    watchToggle->hide();
    tabs->setTabEnabled(2, false);
  }

  friendTypesCombo->setCurrentIndex(0);

  for (int i = 0; i < gridColumns; i++) {
    int stretch = (i == 1) ? 3 : 1;
    friendsLayout->setColumnStretch(i, stretch);
  }

  // remove extra spacing after switching from DW to LJ
  // because it seems to be impossible to remove extra columns from layout
  if (type == LJ_TYPE) {
    for (int i = gridColumns; i < friendsLayout->columnCount(); i++) {
      friendsLayout->setColumnStretch(i, 0);
    }
  }
}

void FriendsManager::open()
{
  if (updated) {
    initializeFriends();
    updated = false;
  }
  show();
}

void FriendsManager::initializeFriends(int index)
{
  // callback from friendTypesCombo and journalTypesCombo
  if (initInProgress) return;
  switch (currentTab) {
  case MODE_USERS:
    initializeFriends();
    break;
  case MODE_TRUSTGROUPS:
    initializeGroups();
    break;
  case MODE_CONTENTFILTERS:
    updateWatchedUsersList();
    break;
  }
}


void FriendsManager::initialize(Server *server_)
{
  initInProgress = true;
  server = server_;
  if (serverType != server->serverType) {
    serverType = server->serverType;
    initializeHeaders(serverType);
  }
  groupsTree->stopSignals();
  initializeFriends();
  initializeGroups();
  if (serverType == DW_TYPE) {
    initializeContentFilters();
  }
  setCursor(currentCursor);
  initInProgress = false;
}

void FriendsManager::initializeFriends()
{
  QSqlQuery query;
  QString command;
  
  if (server == 0) return;

  cleanupFriends();
  usernames.clear();
  flags.clear();

  int friendType = friendTypesCombo->currentIndex();
  if (serverType == LJ_TYPE) {
    switch (friendType) {
    case T_FRIEND:
      whereFriends = "and (trust=1)";
      break;
    case T_MUTUAL:
      whereFriends = "and (trust=1 and trusted=1)";
      break;
    case T_FRIEND_OF:
      whereFriends = "and (trusted=1)";
      break;
    default:
      whereFriends = "";
      break;
    }
  }
  else { // DW_TYPE
    switch (friendType) {
    case T_TRUST_OR_WATCH:
      whereFriends = "and (trust=1 or watch=1)";
      break;
    case T_TRUST_AND_WATCH:
      whereFriends = "and (trust=1 and watch=1)";
      break;
    case T_TRUST:
      whereFriends = "and (trust=1)";
      break;
    case T_TRUSTED:
      whereFriends = "and (trusted=1)";
      break;
    case T_MUTUAL_TRUST:
      whereFriends = "and (trust=1 and trusted=1)";
      break;
    case T_WATCH:
      whereFriends = "and (watch=1)";
      break;
    case T_WATCHED:
      whereFriends = "and (watched=1)";
      break;
    case T_MUTUAL_WATCH:
      whereFriends = "and (watch=1 and watched=1)";
      break;
    case T_TRUSTED_WATCHED:
      whereFriends = "and (trust=1 and watch=1 and trusted=1 and watched=1)";
      break;
    default:
      whereFriends = "";
      break;
    }
  }

  int journalType = journalTypesCombo->currentIndex();
  // 0 - users, 1 - communities, 2 - all
  switch (journalType) {
  case 0:
    whereJournals = "and type=0";
    break;
  case 1:
    whereJournals = "and type=1";
    whereFriends = "";
    break;
  case 2:
    whereJournals = "and type=2";
    whereFriends = "";
    break;
  default:
    whereJournals = "";
    if (whereFriends != "") {
      whereFriends.replace("and (","and ((");
      whereFriends.append(" or type!=0)");
    }
    break;
  }

  int type_ = -1;
  int row = 1;
  int buttonId = 1;
  int width = this->width() / 3; // restrict the width of realname
  int count = 0;
  command = QString("select username, fullname, trust, trusted, watch, watched, type from friends where users_id=%1 %2 %3 order by type, username").arg(server->id).arg(whereFriends).arg(whereJournals);
  // trust - friend trusted by current user
  // watch - friend watched by current user
  // trusted - friend who trusts current user
  // watched - friend who watchs current user
  // on LJ, trust and watch (trusted and watched) are same

  query.exec(command);
  while (query.next()) {
    QString username = query.value(0).toString();
    QString fullname = query.value(1).toString();
    bool trust = query.value(2).toBool();
    bool trusted = query.value(3).toBool();
    bool watch = query.value(4).toBool();   
    bool watched = query.value(5).toBool();   
    bool type = query.value(6).toBool();

    if (type == 0 || type == 1) {
      if (type == 0)
        usernames.insert(buttonId, username);
      int flag = 0;
      if (trust) flag |= 1;
      if (watch) flag |= 2;
      flags.insert(username, flag);
    }

    if (type_ == type) {
      count++;
    }
    else{
      if (type_ != -1){
        QLabel *totalLabel = new QLabel(friendsScrollArea);
        totalLabel->setObjectName("table");
        QString total = QString("Total: %1").arg(count);
        totalLabel->setText(total);
        friendsLayout->addWidget(totalLabel, row, 0, 1, gridColumns);
        friendsLayout->setRowStretch(row, 0);
        row++;
      }
      count = 1;
      if (type == 0) {
        header(headerUsers, 0);
      }
      else if (type == 1) {
        header(headerCommunities, row);
      }
      row++;
      type_ = type;
    }

    LabelMenu *usernameLabel = new LabelMenu(username, friendsScrollArea);
    usernameLabel->setObjectName("table");
    QLabel *fullnameLabel = new QLabel(fullname, friendsScrollArea);
    fullnameLabel->setObjectName("table");
    fullnameLabel->setMaximumWidth(width);

    QFrame *trustFrame = new QFrame(friendsScrollArea); // an additional widget - only to set pretty background 
    trustFrame->setObjectName("table");

    QCheckBox *trustToggle = new QCheckBox(trustFrame);
    trustToggle->setObjectName("table");
    if (type == 0) {      
      trustToggle->setChecked(trust);
      trustButtonGroup->addButton(trustToggle);
      trustButtonGroup->setId(trustToggle, buttonId);
    }
    else if (type == 1) { 
      trustToggle->setChecked(watch); // swapped order: subscribed, then member (LATER)
    }

    QFrame *trustedFrame = new QFrame(friendsScrollArea); // an additional widget - only to set pretty background 
    trustedFrame->setObjectName("table");
    QCheckBox *trustedToggle = new QCheckBox(trustedFrame);
    trustedToggle->setObjectName("table");
    
    if (type == 0) {
      trustedToggle->setChecked(trusted);
    }
    else {
      trustedToggle->setChecked(trust); // member of community; it just uses the same button (LATER)
    }
    trustedToggle->setEnabled(false);

    friendsLayout->addWidget(usernameLabel, row, 0);
    friendsLayout->addWidget(fullnameLabel, row, 1);
    friendsLayout->addWidget(trustFrame, row, 2);
    
    if (serverType == LJ_TYPE || type != 0) {
      friendsLayout->addWidget(trustedFrame, row, 3);
    }
    else {
      friendsLayout->addWidget(trustedFrame, row, 4);

      QFrame *watchFrame = new QFrame(friendsScrollArea);
      watchFrame->setObjectName("table");
      QCheckBox *watchToggle = new QCheckBox(watchFrame);
      watchToggle->setObjectName("table");
      watchToggle->setChecked(watch);
      watchToggle->setEnabled(true);

      watchButtonGroup->addButton(watchToggle);
      watchButtonGroup->setId(watchToggle, buttonId);

      friendsLayout->addWidget(watchFrame, row, 3);

      QFrame *watchedFrame = new QFrame(friendsScrollArea);
      watchedFrame->setObjectName("table");
      QCheckBox *watchedToggle = new QCheckBox(watchedFrame);
      watchedToggle->setObjectName("table");
      watchedToggle->setChecked(watched);
      watchedToggle->setEnabled(false);
      friendsLayout->addWidget(watchedFrame, row, 5);     
    }

    friendsLayout->setRowStretch(row, 0);   
    row++;
    buttonId++;
  }

  QLabel *totalLabel = new QLabel(friendsScrollArea);
  totalLabel->setObjectName("table");
  QString total = QString("Total: %1").arg(count);
  totalLabel->setText(total);
  friendsLayout->addWidget(totalLabel, row, 0, 1, gridColumns);
  friendsLayout->setRowStretch(row, 0);
  row++;

  QLabel *stretch = new QLabel(friendsScrollArea);
  friendsLayout->addWidget(stretch, row, 0, 1, gridColumns);
  friendsLayout->setRowStretch(row, 1);

  updated = false;
}

void FriendsManager::initializeGroups()
{
  QSqlQuery query;
  QString command;

  groupsTree->stopSignals();
  groupsTree->clear();
  groupItems.clear();
  groupsUsers.clear();
  trustGroups.clear();
  usersList->clear();

  QString where;
  int journalType = journalTypesCombo->currentIndex(); 
  // 0 - users, 1 - communities, 2 - all
  switch (journalType) {
  case 0:
    where = "and type=0";
    break;
  case 1:
    where = "and type=1";
    break;
  case 2:
    where = "and type=2";
    break;
  default:
    where = "";
    break;
  }


  command = QString("select name, bit from groups where users_id=%1 and type=0 order by sortorder, name").arg(server->id);
  query.exec(command);
  while (query.next()) {
    QString name = query.value(0).toString();
    int bit = query.value(1).toInt();
    trustGroups.insert(bit, name);
    GroupTreeWidgetItem *item = new GroupTreeWidgetItem(name, bit, groupsTree);
    item->setFlags(item->flags() | Qt::ItemIsEditable);
    groupItems.insert(name, item);
  }

  command = QString("select username, mask, type from friends where users_id=%1 and trust=1 %2 order by username").arg(server->id).arg(where);
  query.exec(command);
  while (query.next()) {
    QString username = query.value(0).toString();
    qulonglong mask = query.value(1).toULongLong();
    int type = query.value(2).toInt();
    new QListWidgetItem(username, usersList);
    
    qulonglong n = 1;
    QMapIterator <int,QString> i(trustGroups);
    while (i.hasNext()) {
      i.next();
      qulonglong shift = (n << i.key());
      if (mask & (n << i.key())) {
        // i.value() is the name of the group
        QTreeWidgetItem *item = new QTreeWidgetItem(groupItems.value(i.value()));
        item->setText(0, username);
        groupsUsers.insertMulti(i.value(), username);
      }
    }     
  }

  groupsTree->startSignals();
}


void FriendsManager::initializeContentFilters()
{
  QSqlQuery query;
  QString command;

  contentFiltersTree->stopSignals();
  contentFiltersTree->clear();
  contentFilterItems.clear();
  contentFiltersUsers.clear();
  contentFilters.clear();

  command = QString("select name, bit, data from groups where users_id=%1 and type=1 order by sortorder, name").arg(server->id);
  query.exec(command);
  while (query.next()) {
    QString name = query.value(0).toString();
    int bit = query.value(1).toInt();
    QString list = query.value(2).toString();
    contentFilters.insert(bit, name);

    GroupTreeWidgetItem *item = new GroupTreeWidgetItem(name, bit, contentFiltersTree);
    item->setFlags(item->flags() | Qt::ItemIsEditable);
    contentFilterItems.insert(name, item);

    QStringList users = list.split(" ",QString::SkipEmptyParts);
    for (int i = 0; i < users.size(); i++) {
      QTreeWidgetItem *itemUser = new QTreeWidgetItem(item);
      itemUser->setText(0, users.at(i));
      contentFiltersUsers.insertMulti(name, users.at(i));
    }
  }

  updateWatchedUsersList();
  contentFiltersTree->startSignals();
}

void FriendsManager::updateWatchedUsersList()
{
  QSqlQuery query;
  QString command;
  
  watchedUsersList->clear();
  
  QString where;
  int journalType = journalTypesCombo->currentIndex(); 
  // 0 - users, 1 - communities, 2 - all
  switch (journalType) {
  case 0:
    where = "and type=0";
    break;
  case 1:
    where = "and type=1";
    break;
  case 2:
    where = "and type=2";
    break;
  default:
    where = "";
    break;
  }

  command = QString("select username, mask from friends where users_id=%1 and watch=1 %2 order by username").arg(server->id).arg(where);
  query.exec(command);
  while (query.next()) {
    QString username = query.value(0).toString();
    qulonglong mask = query.value(1).toULongLong();
    new QListWidgetItem(username, watchedUsersList);
  }


}

bool FriendsManager::doUpdateFriends()
{
  QSqlQuery query;
  QString command;

  updateUserNames.clear();

  // read the current state of DB and compare with the state of checkbuttons
  command = QString("select username, trust, watch from friends where users_id=%1 %2 %3").arg(server->id).arg(whereFriends).arg(whereJournals);
  query.exec(command);

  while (query.next()) {
    QString username = query.value(0).toString();
    int trust = query.value(1).toInt();
    int watch = query.value(2).toInt();

    if (server->serverType == LJ_TYPE) {
      if (trust == 0 && flags.value(username) == 1) {
        updateUserNames.insert(username, 1);
      }
      else if (trust == 1 && (!flags.contains(username) || flags.value(username) == 0)) {
        updateUserNames.insert(username, 0);
      }
    }
    else {
      int trust_ = (flags.value(username) & 1) ? 1 : 0;
      int watch_ = (flags.value(username) & 2) ? 1 : 0;
      if (trust_ != trust || watch_ != watch) {
        updateUserNames.insert(username, flags.value(username));
      }
    }
  }
  return (updateUserNames.count() > 0);
}

void FriendsManager::saveChanges()
{
  if (doUpdateFriends()) {
    server->updateUserName = "";
    server->updateGroupMask = 0;
    server->updateForeground = "";
    server->updateBackground = "";

    server->updateMode = MODE_UPDATE_USER;
    setCursor(Qt::WaitCursor);
    if (server->serverType == LJ_TYPE) {
      server->startUpdateFriends();
    }
    else {
      server->startUpdateCircle();
    }
  }
}

void FriendsManager::updateUser(QString userName, bool trust, bool watch, QString foreground, QString background)
{
  
  updateUserNames.clear();

  int edge = 0;
  if (trust) {
    edge |= 1;
  }
  if (watch && server->serverType == DW_TYPE) {
    edge |= 2;
  }

  updateUserNames.insert(userName, edge);
  server->updateForeground = foreground;
  server->updateBackground = background;
  server->updateUserName = "";
  server->updateGroupMask = 0;
  server->updateMode = MODE_UPDATE_USER;
  setCursor(Qt::WaitCursor);
  if (server->serverType == LJ_TYPE) {
    server->startUpdateFriends();
  }
  else {
    server->startUpdateCircle();
  }

}


void FriendsManager::closeEvent(QCloseEvent *event)
{
  if (doUpdateFriends()) {
    QMessageBox msgBox(this);
    msgBox.setText("Friends list has been modified.");
    msgBox.setInformativeText("Do you want to save your changes?");
    msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
    msgBox.setDefaultButton(QMessageBox::Save);
    int ret = msgBox.exec();
    if (ret == QMessageBox::Save) {
      saveChanges();
      event->ignore(); 
      return;
    }
  }
  updated = true; // to reread the DB on next open()
  event->accept();
}

void FriendsManager::updateData()
{
  QString label = serversCombo->currentText();
  if (label == "-----" || label == "") return;
  
  updateServer(label);
}

void FriendsManager::switchServer(QString label)
{
  for (int i = 0; i < root->servers.size(); i++) {
    if (root->servers.at(i)->label == label) {
      initialize(root->servers.at(i));
      break;
    }
  }
}

void FriendsManager::toggleUserTrust(int id)
{
  bool checked = ((QCheckBox *)trustButtonGroup->button(id))->isChecked();
  QString username = usernames.value(id);
  int flag = flags.value(username);
  if (checked) {
    flag |= 1;
  }
  else {
    flag &= 0xfffe;
  }
  flags.insert(username, flag);
}

void FriendsManager::toggleUserWatch(int id)
{
  bool checked = ((QCheckBox *)watchButtonGroup->button(id))->isChecked();
  QString username = usernames.value(id);
  int flag = flags.value(username);
  if (checked) {
    flag |= 2;
  }
  else {
    flag &= 0xfffd;
  }
  flags.insert(username, flag);
}

void FriendsManager::cleanupFriends()
{
  while (friendsLayout->count() > 0) {
    QWidget *widget = friendsLayout->itemAt(0)->widget();
    friendsLayout->removeWidget(widget);
    delete widget;
  }
  friendsLayout->invalidate();
}

void FriendsManager::header(QList<QString> labels, int row)
{
  int col = 0;
  for (int i = 0; i < labels.count(); i++) {
    QLabel *label = new QLabel(labels.at(i), friendsScrollArea);
    label->setObjectName("header");
    friendsLayout->addWidget(label, row, col++);
    friendsLayout->setRowStretch(row, 0);   
  }
}

void FriendsManager::addGroupStart()
{
  QString name = usernameLine->text();
  if (name == "") return;
  if (groupItems.contains(name)) {
    /* returnPressed() is emitted twice?? better not to have 2 error messages
    QMessageBox msgBox(this);
    msgBox.setIcon(QMessageBox::Warning);
    msgBox.setText("Group already exists!");
    msgBox.exec();
    */
    return;
  }
  int maxGroups = (server->serverType == LJ_TYPE) ? 31 : 60;
  for (int i = 1; i < maxGroups; i++) {
    if (!trustGroups.contains(i)) {
      if (server->updateGroupId != 0) return; //update is in progress
      server->updateGroupName = name;
      server->updateGroupType = 0;
      server->updateGroupId = i;
      server->updateGroupPublic = server->updateGroupSortOrder = -1; // do not change
      server->updateMode = MODE_ADD_GROUP;
      setCursor(Qt::WaitCursor);
      if (server->serverType == LJ_TYPE) {
        server->startUpdateGroups();
      }
      else {
        server->startUpdateCircle();
      }
      break;
    }
  }
  if (server->updateGroupId == 0) {
    QMessageBox msgBox(this);
    QString message = QString(tr("Can't create any more groups - server limit exceeded."));
    msgBox.setText(message);
    msgBox.exec();
  }
}

void FriendsManager::addContentFilterStart()
{
  QString name = usernameLine->text();
  if (name == "") return;
  if (contentFilterItems.contains(name)) {
    return;
  }
  // int maxGroups; not implemented on the server yet?
  server->updateGroupName = name;
  server->updateGroupType = 1;
  server->updateGroupId = -1;
  server->updateGroupPublic = server->updateGroupSortOrder = -1; // do not change
  server->updateMode = MODE_ADD_GROUP;
  setCursor(Qt::WaitCursor);
  server->startUpdateCircle();
}

void FriendsManager::updateGroupStart(GroupTreeWidgetItem *item, int isPublic, int sortOrder)
{
  if (server->updateGroupId != 0) return; //update is in progress FIX LATER! it will ignore the update
  server->updateGroupName = item->text(0); // new name
  server->updateGroupId = item->id;
  server->updateGroupPublic = isPublic;
  server->updateGroupSortOrder = sortOrder;
  updatedItem = item;
  server->updateMode = MODE_UPDATE_GROUP;
  setCursor(Qt::WaitCursor);
  if (currentTab == MODE_TRUSTGROUPS) {
    server->updateGroupType = 0;
    if (server->serverType == LJ_TYPE) {
      server->startUpdateGroups();
    }
    else {
      server->startUpdateCircle();
    }
  }
  else {
    server->updateGroupType = 1;
    server->startUpdateCircle();
  }
}

void FriendsManager::deleteGroupStart(GroupTreeWidgetItem *item)
{
  if (server->updateGroupId != 0) return; //update is in progress
  server->updateGroupName = item->name;
  server->updateGroupId = item->id;
  updatedItem = item;
  server->updateMode = MODE_DELETE_GROUP;
  setCursor(Qt::WaitCursor);
  if (currentTab == MODE_TRUSTGROUPS) {
    server->updateGroupType = 0;
    if (server->serverType == LJ_TYPE) {
      server->startUpdateGroups();
    }
    else {
      server->startUpdateCircle();
    }
  }
  else {
    server->updateGroupType = 1;
    server->startUpdateCircle();
  }
}

void FriendsManager::updateGroupEnd()
{
  switch (server->updateMode) {
  case MODE_ADD_GROUP:
    if (currentTab == MODE_TRUSTGROUPS) {
      GroupTreeWidgetItem *item = new GroupTreeWidgetItem(server->updateGroupName, server->updateGroupId, groupsTree);
      item->setFlags(item->flags() | Qt::ItemIsEditable);
      groupItems.insert(server->updateGroupName, item);
      trustGroups.insert(server->updateGroupId, server->updateGroupName);
      usernameLine->clear();
    }
    else {
      GroupTreeWidgetItem *item = new GroupTreeWidgetItem(server->updateGroupName, server->updateGroupId, contentFiltersTree);
      item->setFlags(item->flags() | Qt::ItemIsEditable);
      item->id = server->updateGroupId; //  new id - created by DW server
      contentFilterItems.insert(server->updateGroupName, item);
      contentFilters.insert(server->updateGroupId, server->updateGroupName);
      usernameLine->clear();
    }
    break;
  case MODE_UPDATE_GROUP:
    if (currentTab == MODE_TRUSTGROUPS) {
      groupItems.insert(server->updateGroupName, updatedItem);
      trustGroups.insert(server->updateGroupId, server->updateGroupName);
    }
    else {
      contentFilterItems.insert(server->updateGroupName, updatedItem);
      contentFilters.insert(server->updateGroupId, server->updateGroupName);
    }
    updatedItem->name = server->updateGroupName;
    break;
  case MODE_DELETE_GROUP:
    delete updatedItem;
    if (currentTab == MODE_TRUSTGROUPS) {
      trustGroups.remove(server->updateGroupId);
    }
    else {
      contentFilters.remove(server->updateGroupId);
    }
    break;
  }
  server->updateGroupId = 0;
  setCursor(currentCursor);
}

void FriendsManager::addUserContentFilterStart(QString groupName, QString userName)
{
  QSqlQuery query;
  QString command;

  command = QString("select bit from groups where users_id=%1 and name=\"%2\" and type=1").arg(server->id).arg(groupName);
  query.exec(command);
  if (query.size() == 0) return; // shouldn't happen
  query.next();
  int bit = query.value(0).toInt();
  server->updateGroupId = bit;
  server->updateGroupName = groupName;
  server->updateUserName = userName;
  server->updateGroupType = 1;
  server->updateMode = MODE_ADD_USER_GROUP;
  setCursor(Qt::WaitCursor);
  server->startUpdateCircle();
}


void FriendsManager::addUserGroupStart(QString groupName, QString userName)
{
  QSqlQuery query;
  QString command;

  command = QString("select mask from friends where users_id=%1 and username=\"%2\"").arg(server->id).arg(userName);
  query.exec(command);
  if (query.size() == 0) return; // shouldn't happen
  query.next();
  server->updateGroupMask = query.value(0).toULongLong();
  
  command = QString("select bit from groups where users_id=%1 and name=\"%2\" and type=0").arg(server->id).arg(groupName);
  query.exec(command);
  if (query.size() == 0) return; // shouldn't happen
  query.next();
  int bit = query.value(0).toInt();
  qulonglong n = 1;
  server->updateGroupMask |= (n << bit);
  server->updateUserName = userName;
  server->updateGroupName = groupName;
  server->updateGroupType = 0;
  server->updateMode = MODE_ADD_USER_GROUP;
  setCursor(Qt::WaitCursor);
  if (serverType == LJ_TYPE) {
    server->startUpdateFriends();
  }
  else {
    server->startUpdateCircle();
  }
}

void FriendsManager::deleteUserGroupStart(QString groupName, QString userName, QTreeWidgetItem *selected)
{
  QSqlQuery query;
  QString command;

  selectedItem = selected;

  int type = (currentTab == MODE_TRUSTGROUPS) ? 0 : 1;
  if (type == 0) {
    command = QString("select mask from friends where users_id=%1 and username=\"%2\"").arg(server->id).arg(userName);
    query.exec(command);
    if (query.size() == 0) return; // shouldn't happen
    query.next();
    server->updateGroupMask = query.value(0).toULongLong();
  }
  
  command = QString("select bit from groups where users_id=%1 and name=\"%2\" and type=%3").arg(server->id).arg(groupName).arg(type);;
  query.exec(command);
  if (query.size() == 0) return; // shouldn't happen
  query.next();
  int bit = query.value(0).toInt();
  server->updateUserName = userName;
  server->updateGroupName = groupName;
  server->updateGroupType = type;
  server->updateMode = MODE_DELETE_USER_GROUP;
  setCursor(Qt::WaitCursor);
  
  if (type == 0) {
    qulonglong n = 1;
    server->updateGroupMask &= ~(n << bit); // set this bit to 0
  }
  else {
    server->updateGroupId = bit;
  }
  if (server->serverType == LJ_TYPE) {
    server->startUpdateFriends();
  }
  else {
    server->startUpdateCircle();
  }
}

void FriendsManager::updateUserGroupEnd()
{
  switch (server->updateMode) {
  case MODE_ADD_USER_GROUP:
    if (currentTab == MODE_TRUSTGROUPS) {
      GroupTreeWidgetItem *parent = groupItems.value(server->updateGroupName);
      QTreeWidgetItem *item = new QTreeWidgetItem(parent);
      parent->setExpanded(true);
      item->setText(0, server->updateUserName);
      groupsUsers.insertMulti(parent->text(0), server->updateUserName);
    }
    else {
      GroupTreeWidgetItem *parent = contentFilterItems.value(server->updateGroupName);
      QTreeWidgetItem *item = new QTreeWidgetItem(parent);
      parent->setExpanded(true);
      item->setText(0, server->updateUserName);
      contentFiltersUsers.insertMulti(parent->text(0), server->updateUserName);
    }
    break;
  case MODE_DELETE_USER_GROUP:
    delete selectedItem;
    if (currentTab == MODE_TRUSTGROUPS) {
      groupsUsers.remove(server->updateGroupName, server->updateUserName);
    }
    else {
      contentFiltersUsers.remove(server->updateGroupName, server->updateUserName);
    }
    break;
  }
  server->updateGroupMask = 0;
  server->updateGroupId = 0;
  setCursor(currentCursor);
}

void FriendsManager::addStart()
{
  switch (currentTab) {
  case MODE_USERS:
    addFriendStart();
    break;
  case MODE_TRUSTGROUPS:
    addGroupStart();
    break;
  case MODE_CONTENTFILTERS:
    addContentFilterStart();
    break;
  }
}

void FriendsManager::addFriendStart()
{
  QString userName = usernameLine->text();
  if (userName == "") return;
  updateUserNames.clear();
  if (server->serverType == LJ_TYPE) {
    updateUserNames.insert(userName, 1);
  }
  else {
    int flag = 0;
    if (trustToggle->isChecked()) {
      flag |= 1;
    }
    if (watchToggle->isChecked()) {
      flag |= 2;
    }
    if (flag == 0) {
      QMessageBox msgBox(this);
      msgBox.setIcon(QMessageBox::Warning);
      msgBox.setText("Please select trust/watch options!");
      msgBox.exec();
      return;
    }
    updateUserNames.insert(userName, flag);
  }

  server->updateGroupMask = 0;
  server->updateGroupId = 0;
  server->updateForeground = "";
  server->updateBackground = "";
  server->updateMode = MODE_UPDATE_USER;
  setCursor(Qt::WaitCursor);
  if (server->serverType == LJ_TYPE) {
    server->startUpdateFriends();
  }
  else {
    server->startUpdateCircle();
  }
}

void FriendsManager::addFriendEnd()
{
  QSqlQuery query;
  QString command;

  QHashIterator<QString, int> i(updateUserNames);
  while (i.hasNext()) {
    i.next();
    if (i.value() == 0) { // remove friends which were deleted (both trust and watch are 0)
      command = QString("delete from friends where users_id=%1 and username=\"%2\"").arg(server->id).arg(i.key());
      query.exec(command);
    }
  }
  usernameLine->clear();
  initialize(server);
}

void FriendsManager::processDropData(QString groupName, QString userName)
{
  if (currentTab == MODE_TRUSTGROUPS) {
    if (!groupsUsers.contains(groupName, userName)) { // if user is not in this group yet
      addUserGroupStart(groupName, userName);
    }
  }
  else { // content filters
    if (!contentFiltersUsers.contains(groupName, userName)) { 
      addUserContentFilterStart(groupName, userName);
    }
  }
}

